//: Playground - noun: a place where people can play

import Cocoa

var str = "Hello, playground"

// protocol for locking/unlocking
protocol LockUnlockProtocol {
    func lock() -> String
    func unlock() -> String
}

class House : LockUnlockProtocol {
    func lock() -> String {
        return "Click!"
    }
    
    func unlock() -> String {
        return "Clack!"
    }
}

class Vehicle : LockUnlockProtocol {
    func lock() -> String {
        return "Beep-Beep!"
    }
    
    func unlock() -> String {
        return "Beep!"
    }
}

let myHouse = House()
myHouse.lock()
myHouse.unlock()

let myCar = Vehicle()
myCar.lock()
myCar.unlock()

// new lock/unlock protocol with locked variable
protocol NewLockUnlockProtocol {
    var locked : Bool { get set }
    func lock() -> String
    func unlock() -> String
}

class Safe : NewLockUnlockProtocol {
    var locked : Bool = false
    
    func lock() -> String {
        locked = true
        return "Ding!"
    }
    
    func unlock() -> String {
        locked = false
        return "Dong!"
    }
}

class Gate : NewLockUnlockProtocol {
    var locked : Bool = false
    
    func lock() -> String {
        locked = true
        return "Clink!"
    }
    
    func unlock() -> String {
        locked = false
        return "Clonk!"
    }
}

let mySafe = Safe()
mySafe.lock()
mySafe.unlock()

let myGate = Gate()
myGate.lock()
myGate.unlock()

// a structure adopting multiple protocols
protocol AreaComputationProtocol {
    func computeArea() -> Double
}

protocol PerimeterComputationProtocol {
    func computePerimeter() -> Double
}

struct Rectangle : AreaComputationProtocol, PerimeterComputationProtocol {
    var width, height : Double
    
    func computeArea() -> Double {
        return width * height
    }
    
    func computePerimeter() -> Double {
        return width * 2 + height * 2
    }
}

var square = Rectangle(width: 3, height: 3)
var rectangle = Rectangle(width: 4, height: 6)

square.computeArea()
square.computePerimeter()

rectangle.computeArea()
rectangle.computePerimeter()

// protocol inheritance
protocol TriangleProtocol : AreaComputationProtocol, PerimeterComputationProtocol {
    var a : Double { get set }
    var b : Double { get set }
    var c : Double { get set }
    var base : Double { get set }
    var height : Double { get set }
}

struct Triangle : TriangleProtocol {
    var a, b, c : Double
    var height, base : Double
    
    func computeArea() -> Double {
        return (base * height) / 2
    }
    
    func computePerimeter() -> Double {
        return a + b + c
    }
}

var triangle1 = Triangle(a: 4, b: 5, c: 6, height: 12, base: 10)

triangle1.computeArea()
triangle1.computePerimeter()

// delegation via protocol
protocol VendingMachineProtocol {
    var coinInserted : Bool { get set }
    func shouldVend() -> Bool
}

class Vendor : VendingMachineProtocol {
    var coinInserted : Bool = false
    
    func shouldVend() -> Bool {
        if coinInserted == true {
            coinInserted = false
            return true
        }
        return false
    }
}

class ColaMachine {
    var vendor : VendingMachineProtocol
    
    init(vendor : VendingMachineProtocol) {
        self.vendor = vendor
    }
    
    func insertCoin() {
        vendor.coinInserted = true
    }
    
    func pressColaButton() -> String {
        if vendor.shouldVend() == true {
            return "Here's a Cola!"
        }
        else {
            return "You must insert a coin!"
        }
    }
    
    func pressRootBeerButton() -> String {
        if vendor.shouldVend() == true {
            return "Here's a Root Beer!"
        }
        else {
            return "You must insert a coin!"
        }
    }
}

var vendingMachine = ColaMachine(vendor : Vendor())

vendingMachine.pressColaButton()
vendingMachine.insertCoin()
vendingMachine.pressColaButton()
vendingMachine.pressColaButton()

// extensions
extension ColaMachine {
    func pressDietColaButton() -> String {
        if vendor.shouldVend() == true {
            return "Here's a Diet Cola!"
        }
        else {
            return "You must insert a coin!"
        }
    }
}

var newVendingMachine = ColaMachine(vendor: Vendor())

vendingMachine.insertCoin()
vendingMachine.pressDietColaButton()

// extending Int to handle memory size designations
extension Int {
    var kb : Int { return self * 1_024 }
    var mb : Int { return self * 1_024 * 1_024 }
    var gb : Int { return self * 1_024 * 1_024 * 1_024 }
}

var x : Int = 4.kb
var y = 8.mb
var z = 2.gb

// extending Double to handle temperature conversions
extension Double {
    var F : Double { return self }
    var C : Double { return (((self - 32.0) * 5.0) / 9.0) }
    var K : Double { return (((self - 32.0) / 1.8) + 273.15) }
}

var temperatureF = 80.4.F
var temperatureC = temperatureF.C
var temperatureK = temperatureF.K

// extending String with functions
extension String {
    func prependString(value : String) -> String {
        return value + self
    }
    
    func appendString(value : String) -> String {
        return self + value
    }
}

"x".prependString("prefix")
"y".appendString("postfix")

// extensions with mutating instance methods
extension Int {
    mutating func triple() {
        self = self * 3
    }
}

var trip = 3
trip.triple()

extension String {
    mutating func decorate() {
        self = "***" + self + "***"
    }
}

var testString = "decorate this"
testString.decorate()

// extension with a closure as a parameter
extension Int {
    func repeater(work : () -> String) {
        for _ in 0..<self {
            work()
        }
    }
}

5.repeater({
    return "repeat this string"
})
